package com.itany.shop.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 黄衡熙
 * @version 1.0
 */
@WebServlet("*.file")
public class FileServlet extends HttpServlet {
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getServletPath();
        switch (path) {
            case "/upload.file":
                upload(req, resp);
                break;
            case "/download.file":
                download(req,resp);
                break;
            case "/download2.file":
                download2(req,resp);
                break;
        }
    }

    protected void upload(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取表单数据,该方式获取不到二进制流数据
//        String username = req.getParameter("username");
//        String file = req.getParameter("file");
//        System.out.println("username:" + username + ",file:" + file);

//        InputStream in = req.getInputStream();
//        byte[] b = new byte[1024];
//        int i = in.read(b);
//        System.out.println(new String(b,0,i));

        // 使用apache提供的第三方依赖帮助开发者进行数据的解析操作
        // 该依赖是专门用于实现数据解析的,而不是文件上传
        // 文件上传只需要表单满足条件即可
        // 此时的二进制数据已经获取到了,只是解析过于麻烦,通过工具简化解析的操作
        // 1.获取解析工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 2.通过解析工厂获取对应的解析器
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            // 3.开始解析
            List<FileItem> fileItems = upload.parseRequest(req);
            Map<String, FileItem> items = new HashMap<>();
            for (FileItem fileItem : fileItems) {
//                System.out.println(fileItem);
//                if("username".equals(fileItem.getFieldName())){
//                    System.out.println("用户名:"+fileItem.getString());
//                } else if("file".equals(fileItem.getFieldName())){
//                    System.out.println("文件名:"+fileItem.getName());
////                    System.out.println("文件内容:"+fileItem.getString());
//                    System.out.println("文件内容:"+fileItem.getString("utf-8"));
//                }
                items.put(fileItem.getFieldName(), fileItem);
            }
            System.out.println("用户名:" + items.get("username").getString());
            System.out.println("文件名:" + items.get("file").getName());
            System.out.println("文件内容:" + items.get("file").getString("UTF-8"));

            // 在执行文件上传时,不允许上传jsp文件
            FileItem file = items.get("file");
            String fileName = file.getName();
            if (fileName.endsWith(".jsp")) {
                req.setAttribute("uploadMsg", "上传失败,不允许上传JSP文件");
                req.getRequestDispatcher("/jsp/upload.jsp").forward(req, resp);
                return;
            }

            // 将文件上传到服务器的指定目录中
            // 在服务器根目录中创建一个专门用于存储上传的文件的目录:upload
            // 每天在upload目录中创建一个以当天年月日命令的子目录,当前的资源存储到对应子目录中
            // 获取服务器根目录,需要通过application来获取
            ServletContext application = getServletContext();
            // getRealPath(path):获取相对于服务器根目录的path目录的绝对地址
            // 如果path为空,则表示获取服务器根目录
            System.out.println("服务器根目录:" + application.getRealPath(""));
            // 如果path不为空,其最终地址本质上就是服务器根路径+path
            String path = application.getRealPath("/upload/" + new SimpleDateFormat("yyyyMMdd").format(new Date()));
            System.out.println("path:" + path);
            File f = new File(path);
            f.mkdirs();

            // 上传文件
            file.write(new File(path, fileName));
        } catch (FileUploadException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    protected void download(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取服务器中需要下载的资源
        String path = req.getServletContext().getRealPath("/js/jquery-3.4.1.min.js");

        try (
                FileInputStream in = new FileInputStream(path);
                OutputStream out = resp.getOutputStream();
        ) {
//            byte[] b = new byte[1024];
//            int i = -1;
//            while ((i = in.read(b)) != -1) {
//                out.write(b, 0, i);
//            }

//            resp.setHeader("content-disposition","attachment");
            resp.setHeader("content-disposition", "attachment;filename=jquery-3.4.1.min.js");
            byte[] b = new byte[1024];
            int i = -1;
            while ((i = in.read(b)) != -1) {
                out.write(b, 0, i);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    protected void download2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取服务器中需要下载的资源
        String path = req.getServletContext().getRealPath("/lib/jstl-1.2.jar");
        try (
                FileInputStream in = new FileInputStream(path);
                OutputStream out = resp.getOutputStream();
        ) {
//            byte[] b = new byte[1024];
//            int i = -1;
//            while ((i = in.read(b)) != -1) {
//                out.write(b, 0, i);
//            }

            // 此时已经可以实现文件的下载,但是在下载时存在两个问题
            // 1.默认是在线打开
            // 2.默认的文件名是当前请求名
            // 可以通过设置响应头信息来处理这些问题
            // 1.可以通过设置content-disposition头信息来指定文件的附件方式
            // 其值有两种:attachment(以附件的方式下载)、inline(在线打开)
//            resp.setHeader("content-disposition","attachment");
            // 2.同样可以通过content-disposition设置文件名
            // 其值的格式为: 附件方式;filename=文件名
//            resp.setHeader("content-disposition","attachment;filename=jstl-1.2.jar");
            // 如果文件名是一个中文名字,需要将其转换成ISO-8859-1的字符集
            resp.setHeader("content-disposition", new String("attachment;filename=中文.jar".getBytes(), "ISO-8859-1"));
            byte[] b = new byte[1024];
            int i = -1;
            while ((i = in.read(b)) != -1) {
                out.write(b, 0, i);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
